﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
using SmartFront.Toolkit.Core;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Caching;

namespace SmartFront.Toolkit.ActionFramework
{
    /// <summary>
    /// Action class represents an action to be performed on 
    /// behalf of the user (usually by calling a web service).  Very 
    /// simply, an action is an object representation of a method call.
    /// Implemented using the command pattern, actions store state about 
    /// method calls like a service call to the server.  Being a class 
    /// also allows polymorphism and the layering of additional 
    /// functionality which surrounds the action method call which is the 
    /// core of the functionality. 
    /// </summary>
    /// <typeparam name="TResult"><typeparamref name="TResult"/> is the .NET 
    /// type of the return parameter of the operation the action wraps
    /// </typeparam>
    /// <example>
    /// The following code shows a hypothetical <c>SampleAction</c> class which
    /// inherits from <see cref="Action{TResult}"/>.
    /// <code>
    /// SampleAction action = new SampleAction();
    /// action.Completed += new ActionCompletedEventHandler&lt;string>(this.HandleActionCompleted);
    /// action.SomeParam = "hello";
    /// action.AnotherParam = 1234;
    /// action.ExecuteAsync();
    /// 
    /// ...
    /// 
    /// void HandleActionComplete(object sender, ActionCompletedEventArgs&lt;string> args)
    /// {
    ///     if(ResultCode.Success == args.ResultCode)
    ///     {
    ///         //method succeeded.
    ///     }
    ///     else if(ResultCode.Error == args.ResultCode)
    ///     {
    ///         //method failed.
    ///     }
    /// }
    /// </code></example>
    /// <remarks>
    /// This class is designed to abstract complex implementation details
    /// such as multithreading and cache support.
    /// <para>This type is thread-safe.</para>
    /// </remarks>
    [Localizable(false),Serializable]
    public abstract class Action<TResult> : IAction<TResult>, IAsynchronousAction<TResult>, ICacheItemRefreshAction, IPendingItem, IDisposable
    {
        #region Members Variables

        /// <summary>
        /// Action Result cache keys which hold by this collection to enumerate later as P and P Default CacheManager not support enumeration of all the cached items!!!
        /// </summary>
        private static readonly Collection<string> actionResultCacheKeys = ActionCacheKeyHelper.ActionCacheKeys;

        /// <summary>
        /// Cache Manager which hold cache for all the actions
        /// </summary>
        private static readonly ICacheManager cacheManager = CacheFactory.GetCacheManager();

        /// <summary>
        /// Action Parameters containing the input parameters for the action
        /// </summary>
        private readonly Hashtable actionParameters = Hashtable.Synchronized(new Hashtable());

        /// <summary>
        /// Get Property - Action Params Order and Name (WORKAROUND: Updated to non-static from static to solve error for same static values being created for different Action classes having the same TResult)
        /// </summary>
        private Dictionary<string, int> actionParametersOrderName;

        /// <summary>
        /// Get Property - Action Params Order and Type (WORKAROUND: Updated to non-static from static to solve error for same static values being created for different Action classes having the same TResult)
        /// </summary>
        private Dictionary<int, Type> actionParametersOrderType;

        /// <summary>
        /// Creats an instance of CacheOptions class
        /// </summary>
        private CacheOptions cacheOptions;

        /// <summary>
        /// Synchronization Event internally used by Actions
        /// </summary>
        [NonSerialized]
        private readonly ManualResetEvent syncEvent = new ManualResetEvent(false);

        /// <summary>
        /// Semaphore
        /// </summary>
        private readonly object syncRoot = new object();

        /// <summary>
        /// Object to maintain the lock
        /// </summary>
        protected object SyncRoot
        {
            get { return this.syncRoot; }
        }

        /// <summary>
        /// Gets or sets the cache options.
        /// </summary>
        /// <value>The cache opt.</value>
        public CacheOptions CacheOptions
        {
            get
            {
                if (null == this.cacheOptions)
                {
                    lock (this.SyncRoot)
                    {
                        if (null == this.cacheOptions)
                        {
                            this.cacheOptions = this.CreateDefaultCacheOptions();
                        }
                    }
                }

                return this.cacheOptions;
            }
        }

        /// <summary>
        /// Gets or sets the wait handle.
        /// </summary>
        /// <value>The wait handle.</value>
        public WaitHandle WaitHandle
        {
            get { return this.syncEvent; }
        }

        /// <summary>
        /// Delegate for method that will be executed synchronously on the UI thread or asynchronously on the worker thread.
        /// </summary>
        private delegate TResult TargetDelegate(object[] parameters);

        #endregion

        #region Constructor

        /// <summary>
        /// Action constructor
        /// </summary>
        protected Action()
        {
            this.SetActionParamsOrderNameType();
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Gets the type of the action params order name.
        /// </summary>
        /// <value>The type of the action params order name.</value>
        protected Dictionary<string, int> ActionParametersOrderName
        {
            get
            {
                {
                    return this.actionParametersOrderName;
                }
            }
        }

        /// <summary>
        /// Gets the type of the action params order type.
        /// </summary>
        /// <value>The type of the action params order type.</value>
        protected Dictionary<int, Type> ActionParametersOrderType
        {
            get
            {
                {
                    return this.actionParametersOrderType;
                }
            }
        }

        /// <summary>
        /// Gets the parameter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <returns></returns>
		[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter",
			Justification = "GetParameter<T> Provides safe mechanisam for get typed value instead of casting after retrival. Refer: http://forums.microsoft.com/msdn/showpost.aspx?postid=805351")]
		protected T GetParameter<T>(string parameterName)
        {
            lock (this.SyncRoot)
            {
                int keyName;
                // Get the exact key (by order) to get the value for the given param 
                if (this.actionParametersOrderName.TryGetValue(parameterName, out keyName))
                {
                    if (this.actionParameters.ContainsKey(keyName))
                    {
                        return (T) this.actionParameters[keyName];
                    }
                }
                return default(T);
            }
        }

        /// <summary>
        /// Sets the parameter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <param name="value">The value.</param>
        protected void SetParameter<T>(string parameterName, T value)
        {
            // It is safe for this thread to read or write
            // from the shared resource.
            int keyName;
            if (this.ActionParametersOrderName.TryGetValue(parameterName, out keyName))
            {
                if (this.actionParameters.ContainsKey(keyName))
                {
                    this.actionParameters[keyName] = value;
                }
                else
                {
                    this.actionParameters.Add(keyName, value);
                }
            }
        }

        /// <summary>
        /// Gets the actual Action method parameter values.
        /// </summary>
        /// <returns>The object array containing the ordered parameters required by the actual action method.</returns>
        protected virtual object[] GetParameterValues()
        {
            lock (this.SyncRoot)
            {
                var objValuesArray = new object[this.actionParametersOrderName.Count];
                for (int i = 0; i < this.actionParametersOrderName.Count; i++)
                {
                    if (this.actionParameters.ContainsKey(i))
                    {
                        objValuesArray[i] = this.actionParameters[i];
                    }
                    else
                    {
                        // If the key is not found, then add the default value for the given parameter type
                        Type paramType = this.actionParametersOrderType[i].GetType();

                        MethodInfo[] methods = typeof (ActionParameterDefaultValueResolver).GetMethods();

                        bool methodFound = false;
                        foreach (MethodInfo method in methods)
                        {
                            if (method.Name == "GetDefaultValue")
                            {
                                methodFound = true;
                                MethodInfo newMethodInfo = method.MakeGenericMethod(paramType);
                                object returnDefault = newMethodInfo.Invoke(null, null);
                                objValuesArray[i] = returnDefault;
                                break;
                            }
                        }

                        if (!methodFound)
                        {
                            objValuesArray[i] = null;
                        }
                    }
                }
                return objValuesArray;
            }
        }

        /// <summary>
        /// Method called by the TargetDelegate when to execute the exact action.
        /// </summary>
        /// <param name="parameters">The object array containing the ordered parameters to be passed to the actual action method to be invoked.</param>
        /// <returns>TResult i.e. the return type of the actual action method.</returns>
        protected abstract TResult ExecuteTarget(object[] parameters);

        
    	/// <summary>
    	/// Gets from cache.
    	/// </summary>
    	/// <returns>
    	/// bool value defining whether the requested
    	/// Item is there in the cache or not based on cache Options
    	/// </returns>
    	protected CacheReturnData<TResult> GetFromCache
    	{
    		get
    		{
                CacheReturnData<TResult> data = new CacheReturnData<TResult> { Result = default(TResult), IsDataCached = false };

    			// Check cache options for override (CacheOptions.Ignore)
                if (this.CacheOptions.Ignore || !this.CacheOptions.Enabled)
                {
                    //CacheTrace.TraceCacheIgnore(key, this.CacheOptions.Enabled);
                    // If not supposed to get from cache return false.
                    return data;
                }

                // Make cache key
                string key = this.MakeCacheKey();

    			// Check cache manager for cache key
				if (cacheManager.Contains(key))
				{
					CacheTrace.TraceCacheHit(key);

					// If found put in result, return true
					data = (CacheReturnData<TResult>) cacheManager.GetData(key);
					data.IsDataCached = true;
					return data;
				}

    			CacheTrace.TraceCacheMiss(key);

    			// If not found return false.
    			return data;
    		}
    	}

    	/// <summary>
        /// Puts the in cache.
        /// This method checks whether cache options is enabled or not and 
        /// if it is enabled then add the result in to Cache
        /// </summary>
        /// <param name="result">The result.</param>
        protected void PutInCache(TResult result)
        {
            try
            {
                //Take Appropriate action based on the Caching related Attributes applied on the action
                this.HandleCustomCachingAttributes(result);

                // Check cache options if should cache (CacheOptions.Enabled)
                if (this.CacheOptions.Enabled)
                {
                    // Make cache key
                    string key = this.MakeCacheKey();

                    var expirationPolicies = new ICacheItemExpiration[this.CacheOptions.ExpirationPolicy.Count];
                    this.CacheOptions.ExpirationPolicy.CopyTo(expirationPolicies, 0);

                    ICacheItemRefreshAction refreshAction = this.CacheOptions.AutoRefresh ? this : null;

                    CacheReturnData<TResult> cachedData = new Action<TResult>.CacheReturnData<TResult>()
                        {
                            IsDataCached = true,
                            Result = result,
                            SyncObject = new object()
                        };
                    //pass "this" as the autorefresh object if autorefresh is enabled
                    cacheManager.Add(key, cachedData, this.CacheOptions.ScavengingPriority, refreshAction, expirationPolicies);

                    //Add cache key to our action cache key collection for later to remove from cache when its dependent action will call
                    if (!actionResultCacheKeys.Contains(key))
                    {
                        actionResultCacheKeys.Add(key);
                    }
                    //If our key collection items is not in sync with cache manager cached items than remove from our collection for consistency
                    SyncActionCacheKeyCollection();

                    CacheTrace.TraceCacheAdd(key);
                }
            }
            catch(Exception ex)
            {
				//ILogger logger = UnityWrapper.Default.Resolve<ILogger>();
				//if (null != logger)
				//{
				//    logger.Log(ex);
				//}
            }
        }

/*
        private void UpdateDependentCache(TResult result)
        {
            object[] dependentActions = this.GetType().GetCustomAttributes(typeof (UpdateCacheDependencyAttribute),
                                                                           false);
            if (null != dependentActions && dependentActions.Length > 0)
            {
                foreach (UpdateCacheDependencyAttribute attribute in dependentActions)
                {
                    this.HandleCacheUpdate(attribute, result);
                }
            }
        }
*/

        /// <summary>
        /// Syncs the action cache key collection.
        /// </summary>
        private static void SyncActionCacheKeyCollection()
        {
            if (actionResultCacheKeys.Count > cacheManager.Count)
            {
                var keysToRemove = new Collection<string>();
                foreach (string key in actionResultCacheKeys)
                {
                    if (!cacheManager.Contains(key))
                    {
                        keysToRemove.Add(key);
                    }
                }
                foreach (string key in keysToRemove)
                {
                    if (actionResultCacheKeys.Contains(key))
                    {
                        actionResultCacheKeys.Remove(key);
                    }
                }
            }
        }

        /// <summary>
        /// Configurs the cache.This methods sets the default
        /// values of CacheOptions
        /// </summary>
        protected virtual CacheOptions CreateDefaultCacheOptions()
        {
            var options = new CacheOptions
            {
                Enabled = false,
                Ignore = false,
                ScavengingPriority = CacheItemPriority.Normal,
                AutoRefresh = false
            };

            return options;
        }

        /// <summary>
        /// This current method gets all the properties from the derived Action class (retrived through reflection)
        /// </summary>
        /// <returns>The type information about properties for the given derived action type.</returns>
        protected virtual PropertyInfo[] GetCacheKeyProperties()
        {
            Type currentActionType = this.GetType();
            PropertyInfo[] propertyInfoArray = currentActionType.GetProperties();

            return (from propInfo in propertyInfoArray
                    let propAttributes = propInfo.GetCustomAttributes
                    (typeof (OperationParameterInfoAttribute), false)
                    where 1 == propAttributes.Length
                    select propInfo).ToArray();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Expires the dependent actions cache.
        /// </summary>
        private void HandleCustomCachingAttributes(TResult result)
        {
            object[] dependentActions = this.GetType().GetCustomAttributes(false);
            if (null != dependentActions && dependentActions.Length > 0)
            {
                foreach (Attribute attribute in dependentActions)
                {
                	DependentActionAttribute dependentActionAttribute = attribute as DependentActionAttribute;
					if (null != dependentActionAttribute)
                    {
						HandleCacheExpiration(dependentActionAttribute);
                    }
                }
            }
        }

        private static void HandleCacheExpiration(DependentActionAttribute attribute)
        {
            string dependentActionName = attribute.DependentActionType.Name;
            Collection<string> dependentActionCacheKey =
                actionResultCacheKeys.Where(row => row.StartsWith(dependentActionName, StringComparison.Ordinal)).
                    ToCollection();
            foreach (string key in dependentActionCacheKey)
            {
                if (cacheManager.Contains(key))
                {
                    cacheManager.Remove(key);
                }
                if (actionResultCacheKeys.Contains(key))
                {
                    actionResultCacheKeys.Remove(key);
                }
            }
        }

        /// <summary>
        /// Makes the cache key.
        /// This method will generate the key which is used
        /// to add the result in to Cache and also to get that data from 
        /// Cache.
        /// </summary>
        /// <returns>a string value of generated key</returns>
        private string MakeCacheKey()
        {
            var sb = new StringBuilder();

            //NOTE: DO NOT CHANGE BELOW 2 LINES OF CODE... AS ITS REQUIRED TO EXPIRE THOSE CACHE WHEN DEPENDENT ACTION
            //CALLED SO TO FIND OUT WHAT ARE THE DEPENDENT ACTIONS CACHE IN THE CACHE MANAGER COLLECTION.
            //SO REQUIRED TO HAVE ACTION NAME AS FIRST IN THE KEY OF CACHE
            //prepend the name of the action first.
            Type currentActionType = this.GetType();
            sb.Append(currentActionType.Name);

            PropertyInfo[] propertyInfoArray = this.GetCacheKeyProperties();
            foreach (PropertyInfo prop in propertyInfoArray)
            {
                sb.Append("&");
                sb.Append(prop.Name);
                sb.Append("=");

                Object val = prop.GetValue(this, null);
                if (null == val)
                {
                    sb.Append("null");
                }
                else
                {
                    var enumerable = val as IEnumerable;
                    if (null != enumerable)
                    {
                        string hashCode = enumerable.Cast<object>()
                            .Aggregate(String.Empty, (current, item) => current + item.GetHashCode()
                                .ToString(CultureInfo.InvariantCulture));

                        sb.Append(hashCode.ToString(CultureInfo.InvariantCulture));
                    }
                    else
                    {
                        sb.Append(val.GetHashCode().ToString(CultureInfo.InvariantCulture));
                    }
                }
            }

            return sb.ToString();
        }

/*
        /// <summary>
        /// This method returns the hashcode for the ReferenceType objects.
        /// </summary>
        /// <param name="val">ReferenceType object</param>
        /// <returns>Hashcode</returns>
        private int GetHashCodeForReferenceTypes(object val)
        {
            int hashcode = 0;
            var enumerableVal = val as IEnumerable;
            if (null != enumerableVal)
            {
                hashcode = this.GetHashCodeForEnumerableTypes(enumerableVal);
            }
            else
            {
                Type currentType = val.GetType();
                PropertyInfo[] propertyInfoArray = currentType.GetProperties();
                foreach (PropertyInfo propInfo in propertyInfoArray)
                {
                    Object propertyVal = propInfo.GetValue(val, null);
                    if (null != propertyVal)
                    {
                        hashcode = propertyVal.GetHashCode() ^ hashcode;
                    }
                }
            }
            return hashcode;
        }
*/

/*
        /// <summary>
        /// This method returns the hashcode for the IEnumerable objects.
        /// </summary>
        /// <param name="val">IEnumerable object</param>
        /// <returns>Hashcode</returns>
        private int GetHashCodeForEnumerableTypes(IEnumerable val)
        {
            int hashcode = 0;
            foreach (object item in val)
            {
                hashcode = item.GetHashCode() ^ hashcode;
            }
            return hashcode;
        }
*/

        /// <summary>
        /// Set Action Params order name and type
        /// </summary>
        private void SetActionParamsOrderNameType()
        {
            lock (this.SyncRoot)
            {
                if (null == this.actionParametersOrderName)
                {
                    this.actionParametersOrderName = new Dictionary<string, int>();
                    this.actionParametersOrderType = new Dictionary<int, Type>();
                    Type typeInfo = this.GetType();

                    // Read the properties of the Action instance
                    PropertyInfo[] paramInfoArr = typeInfo.GetProperties();

                    // Fill the action parameter name and types in the dictionary - (in the correct order as the dictionary key)
                    foreach (PropertyInfo paramInfo in paramInfoArr)
                    {
                        object[] propAttributes = paramInfo.GetCustomAttributes(typeof (OperationParameterInfoAttribute), false);
                        if (propAttributes.Length == 1)
                        {
                            var operationParam = propAttributes[0] as OperationParameterInfoAttribute;
                            if (operationParam != null) {
                                this.actionParametersOrderName.Add(paramInfo.Name, operationParam.Order);
                                this.actionParametersOrderType.Add(operationParam.Order, paramInfo.PropertyType);
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region IAction<TResult> Methods

        /// <summary>
        /// Executes the actions.  This method will return synchronously,
        /// but it executes the action internally to actually perform the
        /// action on a background thread.
        /// </summary>
        /// <remarks>
        /// After the <see cref="Action{TResult}"/> has been executed, the properties
        /// of the <see cref="Action{TResult}"/> become read-only.  Because the action
        /// executes on a background thread changing properties after execution
        /// would cause indeterminate results.
        /// </remarks>
        public TResult Execute()
        {
        	CacheReturnData<TResult> cacheReturnData = this.GetFromCache;

        	// Checks if the service operation result is in cache
            if (!cacheReturnData.IsDataCached)
            {
                object[] parameters = this.GetParameterValues();
                TResult result = this.ExecuteTarget(parameters);
                this.PutInCache(result);
            	return result;
            }

            return cacheReturnData.Result;
        }

        public virtual object CacheSyncObject
        {
            get
            {
                //if(!this.CacheOptions.Enabled)
                //    throw new InvalidOperationException("Cannot acess action cache synchronization object, Action Caching is not enabled for this action.");
                CacheReturnData<TResult> cachedData = this.GetFromCache;
                if (cachedData.IsDataCached)
                    return cachedData.SyncObject;
                return null;
                //throw new InvalidOperationException("Trying to access action cache syncronization object before calling action");
            }
        }

        #endregion

        #region IAsynchronousAction<TResult> Members

        /// <summary>
        /// Executes the action asynchronously
        /// </summary>
        public void ExecuteAsync()
        {
        	CacheReturnData<TResult> cacheReturnData = this.GetFromCache;

        	// Checks if the service operation result is in cache
            if (cacheReturnData.IsDataCached)
            {
				this.PostSuccessed(cacheReturnData.Result);
                return;
            }

            Guid taskId = Guid.NewGuid();
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);

            // Delegate that invokes the method.
            var target = new TargetDelegate(this.ExecuteTarget);
            object[] parameters = this.GetParameterValues();

            target.BeginInvoke(parameters, delegate(IAsyncResult asyncResult)
            {
                TResult res = default(TResult);
                bool isSuccess;
                try
                {
                    res = target.EndInvoke(asyncResult);
                    //raise the completed event on calling thread.                    
                    asyncOp.PostOperationCompleted(this.PostSuccessed, (object)res);
                    isSuccess = true;
                }
                catch (Exception ex)
                {
                    //raise the completed event on calling thread.
                    asyncOp.PostOperationCompleted(this.PostFailed, ex);
                    isSuccess = false;
                }

                if (isSuccess)
                {
                    this.PutInCache(res);
                }
            }, null);
        }

        /// <summary>
        /// Raised when the processing of the action has successfully completed.
        /// </summary>
        /// <remarks>This is guarenteed to be raised in the same
        /// <see cref="SynchronizationContext"/> which executed the
        /// <see cref="Execute"/> method.  For a WPF UI thread, this 
        /// will be the <see cref="DispatcherSynchronizationContext"/>.
        /// </remarks>
		public event EventHandler<ActionResultEventArgs<TResult>> Completed;

        #endregion

        #region ICacheItemRefreshAction Members

        /// <summary>
        /// Called when an item expires from the cache. This method can be used to notify an application that
        /// the expiration occured, cause the item to be refetched and refreshed from its original location, or
        /// perform any other application-specific action.
        /// </summary>
        /// <param name="removedKey">Key of item removed from cache. Will never be null.</param>
        /// <param name="expiredValue">Value from cache item that was just expired</param>
        /// <param name="removalReason">Reason the item was removed from the cache. See <see cref="T:Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemRemovedReason"/></param>
        /// <remarks>This method should catch and handle any exceptions thrown during its operation. No exceptions should leak
        /// out of it.</remarks>
		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes"),  
		SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expiredValue"),
         SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")]
        void ICacheItemRefreshAction.Refresh(string removedKey, object expiredValue,
                                             CacheItemRemovedReason removalReason)
        {
            this.Refresh(removedKey, expiredValue, removalReason);
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
		[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
			Justification = "This method calls protected Dispose(bool) method which calls GC.SuppressFinalize(object).")]
		public void Dispose()
        {
            this.Dispose(true);
        }

        #endregion

        #region IPendingItem Members

        /// <summary>
        /// Occurs when the pending item is ready to be unregistered.
        /// </summary>
        public event EventHandler Unregister;

        #endregion

        /// <summary>
        /// Raises the event after Action is completed
        /// </summary>
        /// <param name="args">The <see cref="SmartFront.Toolkit.ActionFramework.ActionSuccessEventArgs&lt;TResult&gt;"></see>
        ///   instance containing the event data.</param>
        protected virtual void OnCompleted(ActionResultEventArgs<TResult> args)
        {
            //this.correlationManager.StopLogicalOperation();
            if (null != this.Completed)
            {
                this.Completed(this, args);
            }

            //signal to waiting threads that operation has completed.
            //FIX: only signal after posting event.
            this.syncEvent.Set();
        }

        /// <summary>
        /// When the async action is completed this method gets called on the correct thread
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void PostSuccessed(object result)
        {
			var e = new ActionResultEventArgs<TResult>((TResult)result);

			this.OnCompleted(e);
            this.OnUnregister(EventArgs.Empty);
        }

        /// <summary>
        /// When the async action is completed this method gets called on the correct thread
        /// </summary>
        /// <param name="operationState">State of the operation.</param>
        private void PostFailed(object exception)
        {
			var e = new ActionResultEventArgs<TResult>((Exception)exception);

			this.OnCompleted(e);
            this.OnUnregister(EventArgs.Empty);
        }

        /// <summary>
        /// Called when an item expires from the cache. This method can be used to notify an application that
        /// the expiration occured, cause the item to be refetched and refreshed from its original location, or
        /// perform any other application-specific action.
        /// </summary>
        /// <param name="removedKey">Key of item removed from cache. Will never be null.</param>
        /// <param name="expiredValue">Value from cache item that was just expired</param>
        /// <param name="removalReason">Reason the item was removed from the cache. See <see cref="T:Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemRemovedReason"/></param>
        /// <remarks>This method should catch and handle any exceptions thrown during its operation. No exceptions should leak
        /// out of it.</remarks>
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expiredValue"),
         SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods"),
		SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", 
			Justification = "Here action automatically called by Enterprise Library cache manager which trigger action again, " 
			+ " there may be any kind of exception expected based on derived action.")]
        protected void Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
        {
            CacheTrace.TraceCacheRemoved(removedKey, removalReason);

            if (CacheItemRemovedReason.Expired == removalReason)
            {
                try
                {
                    //refetch the data an put it back in the cache.
                    TResult result = this.ExecuteTarget(this.GetParameterValues());
                    cacheManager.Add(removedKey, result);
                }
                catch (Exception ex) 
                {
                    Debug.WriteLine("Exception on refresh cache:"  + ex);
                }
            }
        }

        /// <summary>
        /// Raises the event.
        /// </summary>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnUnregister(EventArgs args)
        {
            if (null != this.Unregister)
            {
                this.Unregister(this, args);
            }
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly", 
			Justification = "This method alwayz invoke via IDisposable.Dispose method, will give better implementation for child classes.")]
		protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (null != this.syncEvent)
                {
                    this.syncEvent.Close();
                }

				GC.SuppressFinalize(this);
            }
        }

		/// <summary>
		/// Cache Return Data 
		/// </summary>
        /// <typeparam name="TData">The type of the result.</typeparam>
		protected class CacheReturnData<TData>
		{
			/// <summary>
			/// Gets or sets the result.
			/// </summary>
			/// <value>The result.</value>
			public TData Result { get; set; }

			/// <summary>
			/// Gets or sets a value indicating whether this instance is data cached.
			/// </summary>
			/// <value>
			/// 	<c>true</c> if this instance is data cached; otherwise, <c>false</c>.
			/// </value>
			public bool IsDataCached { get; set; }

            public object SyncObject { get; set; }
		}
    }
}